//-----------------------------------------------------------------------
// <copyright file="AddComponent.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Andy Reeves [2008-02-25: M FOurie - code cleanup and codeplex feedback</author>
// <email>andyr</email>
// <date>2004-03-23</date>
// <summary>Adds a component to the specified ComponentServices Application.</summary>
//-----------------------------------------------------------------------
namespace Microsoft.Sdc.Tasks.ComponentServices.Application
{
    using System;
    using System.IO;
    using System.Globalization;
    using Microsoft.Win32;
    using Microsoft.Sdc.Tasks.Configuration.ComponentServices;
    using Microsoft.Build.Framework;
    using Microsoft.Sdc.Tasks.Configuration;

    /// <summary>
    /// Adds a component to the specified ComponentServices Application.
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[<ComponentServices.Application.AddComponent Path="path" ApplicationName="applicationName" ComponentName="componentName" ConstructorString="constructorString" Identity="identity" Password="password" Runtime="runtime" ShutdownAfter="shutDownAfter" ActivationString="activationString" EnforceAccessChecks="enforceAccessChecks" RunForever="runForever" />]]></code>
    /// <para>where:</para>
    /// <para><i>path (Required)</i></para>
    /// <para>The valid path to the DLL to be added to application.</para>
    /// <para><i>applicationName</i></para>
    /// <para>The name of the COM+ application. This application will be created if it does not exist.</para>
    /// <para><i>identity</i></para>
    /// <para>Sets the server process identity for the application. Specify a valid user account or "Interactive User" to have the application assume the identity of the current logged-on user. This is not enabled for library applications, which run in the client process.</para>
    /// <para><i>password</i></para>
    /// <para>Sets the password used by the server process to log on under the identity. Password should be set at the same time as Identity, because the password and identity are validated before being saved. If the password and identity get out of sync, the application cannot be launched until they are reset by an administrator.</para>
    /// <para><i>runtime</i></para>
    /// <para>Decimal value for version of runtime targeted. Mandatory for .NET components to be registered. Defaults to "2.0"</para>
    /// <para><i>shutDownAfter</i></para>
    /// <para>Sets the delay before shutting down a server process after it becomes idle. Shutdown latency ranges from 0 to 1440 minutes (24 hours). If RunForever is set to True, this property is ignored. ShutdownAfter is not enabled for library (in-process) applications.</para>
    /// <para><i>activationString</i></para>
    /// <para>Set to "Local" to indicate that objects within the application run within a dedicated local server process (server application). Set to "Inproc" to indicate that objects run in their creator's process (library application). Defaults to local</para>
    /// <para><i>enforceAccessChecks</i></para>
    /// <para>Indicates whether access checks are performed for the application when clients make calls into it.</para>
    /// <para><i>runForever</i></para>
    /// <para>Enables a server process to continue if an application is idle. If set to True, the server process does not shut down when left idle. If set to False, the process shuts down according to the value set by the ShutdownAfter property. RunForever is not enabled for library (in-process) applications.</para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test" >
    ///         <ComponentServices.Application.AddComponent
    ///             Path="C:\Soutions\MyNamespace.MyAssembly.dll"
    ///             ApplicationName="MyApplicationName"
    ///             Identity="Interactive User"
    ///             Runtime="1.1"
    ///             ActivationString="Local"
    ///             RunForever="true" />
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    public class AddComponent : TaskBase
    {
        private string path;
        private string applicationName;
        private string activationString;
        private string identity;
        private string password;
        private string runtime = String.Empty;
        private bool enforceAccessChecks;
        private bool runForever;
        private string shutdownAfter;

        #region Public Properties

        /// <summary>
        /// Gets or sets the name of the COM+ Application.
        /// </summary>
        /// <value>The COM+ Application name. The application will be created if it doesn't exist</value>
        public string ApplicationName
        {
            get { return this.applicationName; }
            set { this.applicationName = value; }
        }

        /// <summary>
        /// Gets or sets the path to the DLL to be added to the application
        /// </summary>
        /// <value>Defaults to String.Empty</value>
        [Required]
        public string Path
        {
            get { return this.path; }
            set { this.path = value; }
        }

        /// <summary>
        /// Gets or sets the server process identity for the application. 
        /// </summary>
        /// <value>Specify a valid user account or "Interactive User" to have the application assume the identity of the current logged-on user.</value>
        public string Identity
        {
            get { return this.identity; }
            set { this.identity = value; }
        }

        /// <summary>
        /// Gets or sets the password for the supplied identity
        /// </summary>
        /// <value>The password for the user account specified in Identity. Not required if the Identity is set to "Interactive User".</value>
        public string Password
        {
            get { return this.password; }
            set { this.password = value; }
        }

        /// <summary>
        /// Gets or sets the Version of the .NET runtime targeted.
        /// </summary>
        /// <value>Decimal value for version of runtime targeted. Defaults to "1.0"</value>
        public string Runtime
        {
            get { return this.runtime; }
            set { this.runtime = value; }
        }

        /// <summary>
        /// Gets or sets the Time in minutes for an application to wait before shutting.
        /// </summary>
        /// <value>Sets the delay before shutting down a server process after it becomes idle. Shutdown latency ranges from 0 to 1440 minutes (24 hours). If RunForever is set to True, this property is ignored. ShutdownAfter is not enabled for library (in-process) applications.</value>
        public string ShutdownAfter
        {
            get { return this.shutdownAfter; }
            set { this.shutdownAfter = value; }
        }

        /// <summary>
        /// Gets or sets the type of activation for the application.
        /// </summary>
        /// <value>"Local" - local activation indicates that objects within the application run within a dedicated local server process (server application). 
        /// "Inproc" - in-process activation indicates that objects run in their creator's process (library application). 
        /// Defaults to "Local".</value>
        public string ActivationString
        {
            get { return this.activationString; }
            set { this.activationString = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether access checks are performed for the application when clients make calls into it.
        /// </summary>
        /// <value>Set to true if access checks are to be performed</value>
        public bool EnforceAccessChecks
        {
            get { return this.enforceAccessChecks; }
            set { this.enforceAccessChecks = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether a server process should continue if an application is idle. 
        /// </summary>
        /// <value>If set to True, the server process does not shut down when left idle. If set to False, the process shuts down according to the value set by the ShutdownAfter property. RunForever is not enabled for library (in-process) applications.</value>
        public bool RunForever
        {
            get { return this.runForever; }
            set { this.runForever = value; }
        }

        #endregion

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            #region Execute code

            Activation activation = Activation.Local;

            FileInfo fi = new FileInfo(this.path);
            if (fi.Exists == false)
            {
                Log.LogError(string.Format("Path not found: {0}: ", this.Path));
                return;
            }

            Log.LogMessageFromResources("ComponentServices.AddingComponent", fi.Name);

            // detect if its a .NET assembly or not
            if (Utilities.IsValidAssemblyFile(fi.FullName))
            {
                string version = "";
                if (this.Runtime.Length == 0 || this.Runtime == "2.0")
                {
                    version = "v2.0.50727";
                }
                else if (this.Runtime == "1.1")
                {
                    version = "v1.1.4322";
                }
                else if (this.Runtime == "1.0")
                {
                    version = "v1.0.3705";
                }

                RegistryKey runtimeKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework");
                string pathToFramework = Convert.ToString(runtimeKey.GetValue("InstallRoot"), CultureInfo.InvariantCulture);
                runtimeKey.Close();

                ShellExecute shellExecute = new ShellExecute();
                shellExecute.Filename = System.IO.Path.Combine(System.IO.Path.Combine(pathToFramework, version), "regsvcs.exe");
                if (string.IsNullOrEmpty(this.ApplicationName))
                {
                    shellExecute.Arguments = String.Format(CultureInfo.InvariantCulture, @"/quiet ""{0}""", this.path);
                }
                else
                {
                    shellExecute.Arguments = String.Format(CultureInfo.InvariantCulture, @"/quiet ""{0}"" ""{1}""", this.path, this.applicationName);
                }

                int exitcode = shellExecute.Execute();

                if (exitcode != 0)
                {
                    Log.LogError("Shell execute failed: " + shellExecute.StandardOutput);
                }
                else
                {
                    if (string.IsNullOrEmpty(this.activationString) == false)
                    {
                        activation = (Activation)Enum.Parse(typeof(Activation), this.activationString, true);
                    }

                    Utilities.UpdateApplication(this.applicationName, this.identity, this.password, this.enforceAccessChecks, activation, this.runForever, this.shutdownAfter);
                }
            }
            else
            {
                Log.LogMessageFromResources(MessageImportance.Low, "ComponentServices.AboutToCallAddComponent", this.path);
                Application.AddComponent(this.path, this.applicationName);
                Log.LogMessageFromResources(MessageImportance.Low, "ComponentServices.AddComponentCompleted", this.path);

                if (string.IsNullOrEmpty(this.activationString) == false)
                {
                    activation = (Activation)Enum.Parse(typeof(Activation), this.activationString, true);
                }

                Utilities.UpdateApplication(this.applicationName, this.identity, this.password, this.enforceAccessChecks, activation, this.runForever, this.shutdownAfter);
            }

            #endregion
        }
    }
}